package com.hyopt.common.base;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hyopt.common.exception.MyException;
import com.hyopt.common.validator.ValidatorUtils;
import com.hyopt.common.validator.group.AddGroup;
import com.hyopt.common.validator.group.UpdateGroup;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
 * 基础实现类接口
 *
 * @author wanglei
 * @date 2019-06-06 13:47:46
 */
public abstract class BaseServiceImpl<M extends BaseMapper<T>, T, N extends BaseParamsForm> extends ServiceImpl<M, T> implements BaseService<T, N> {

	public <V> ConvertFun<Object, V> getDefaultConvertFun(Class<V> clazz) {
		return null;
	}

	@Override
	public <V> V queryVoById(Long id, Class<V> clazz, ConvertFun<Object, V>... funArray) {
		T entity = this.getById(id);
		Assert.notNull(entity, getEntityName() + "不存在");
		return BaseServiceUtils.toBeanAndCovert(entity, clazz, funArray);
	}

	@Override
	public <V> V queryVoById(Long id, Class<V> clazz) {
		return queryVoById(id, clazz, getDefaultConvertFun(clazz));
	}

	@Override
	public <V> V queryVoByIdAndStatus(Long id, Class<V> clazz, boolean status) {
		return queryVoByIdAndStatus(id, clazz, status, getDefaultConvertFun(clazz));

	}

	@Override
	public <V> V queryVoByIdAndStatus(Long id, Class<V> clazz, boolean status, ConvertFun<Object, V>... funArray) {
		T entity = this.getOne(
				new QueryWrapper<T>()
						.eq(getIdKey(), id)
						.eq("status", status)
		);
		Assert.notNull(entity, getEntityName() + "不存在");
		return BaseServiceUtils.toBeanAndCovert(entity, clazz, funArray);
	}

	@Override
	public <V> List<V> queryVoList(QueryWrapper<T> wrapper, Class<V> clazz) {
		List<T> list = this.list(wrapper);
		return convertToListVo(list, clazz);
	}

	@Override
	public <V> List<V> queryVoList(QueryForm<N> form, Class<V> clazz) {
		List<T> list = this.list(this.getSelectWrapper(form, true));
		return convertToListVo(list, clazz);
	}

	@Override
	public <V> List<V> convertToListVo(List list, Class<V> clazz) {
		return convertToListVo(list, clazz, getDefaultConvertFun(clazz));
	}

	@Override
	public <V, M> List<V> convertToListVo(List<M> list, Class<V> clazz, ConvertFun<Object, V>... funArray) {
		return BaseServiceUtils.convertToListVo(list, clazz, funArray);
	}

	protected boolean isFieldClassTrue(Class beanClazz, String key, Class valueClass, Class... argumentClass) {
		return BaseServiceUtils.isFieldClssTrue(beanClazz, key, valueClass, argumentClass);
	}


	@Override
	public <V> IPage queryVoPage(IPage<T> iPage, QueryWrapper<T> wrapper, Class<V> clazz) {
		iPage = this.page(iPage, wrapper);
		return convertToPageVo(iPage, clazz);
	}

	@Override
	public <V> IPage<V> queryMapVoPage(IPage<T> iPage, QueryWrapper<T> wrapper, Class<V> clazz) {
		IPage page = this.pageMaps(iPage, wrapper);
		return convertToPageVo(page, clazz);
	}

	@Override
	public <V> IPage<V> queryVoPage(QueryForm<N> form, Class<V> clazz) {
		IPage<T> iPage = form.getIPage(this.currentModelClass());
		return queryVoPage(iPage, this.getSelectWrapper(form, false), clazz);
	}

	@Override
	public <V> IPage<V> convertToPageVo(IPage iPage, Class<V> clazz) {
		return convertToPageVo(iPage, clazz, getDefaultConvertFun(clazz));
	}

	@Override
	public <V, M> IPage<V> convertToPageVo(IPage<M> iPage, Class<V> clazz, ConvertFun<Object, V>... funArray) {
		return BaseServiceUtils.convertToPageVo(iPage, clazz, funArray);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public T saveForm(Long userId, Object form) {
		ValidatorUtils.validateEntity(form, AddGroup.class);
		T entity = saveFormToEntity(userId, form);
		return this.saveOrUpdateEntity(entity);
	}

	public T saveFormToEntity(Long userId, Object form) {
		T entity = BeanUtil.toBean(form, this.currentModelClass());
		BeanUtil.setFieldValue(entity, "createuserid", userId);
		BeanUtil.setFieldValue(entity, "createtime", new Date());
		return entity;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public T updateForm(Long userId, Object form) {
		ValidatorUtils.validateEntity(form, UpdateGroup.class);
		T entity = updateFormToEntity(userId, form);
		return this.saveOrUpdateEntity(entity);
	}

	public T updateFormToEntity(Long userId, Object form) {
		T entity = BeanUtil.toBean(form, this.currentModelClass());
		BeanUtil.setFieldValue(entity, "updateUserId", userId);
		BeanUtil.setFieldValue(entity, "updateTime", new Date());
		return entity;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public T saveOrUpdateEntity(T entity) {
		if (ObjectUtil.isNull(entity)) {
			throw new MyException(getEntityName() + "不能为null");
		}
		initSaveOrUpdateEntity(entity);
		T newEntity;
		String id = Convert.toStr(BeanUtil.getFieldValue(entity, getIdKey()));
		boolean isNew = true;
		if (ObjectUtil.isNull(id)) {
			newEntity = entity;
			beforeSaveEntity(entity);
		} else {
			isNew = false;
			T old = this.getById(id);
			if (ObjectUtil.isNull(old)) {
				throw new MyException(getEntityName() + "已被删除，无法修改");
			}
			newEntity = ObjectUtil.clone(old);
			BeanUtil.copyProperties(entity, newEntity, CopyOptions.create(this.currentModelClass(), true, ArrayUtil.toArray(getUpdateIgnoreKey(), String.class)));
			beforeUpdateEntity(newEntity, old);
		}
		this.beforeSaveOrUpdateEntity(newEntity, isNew);
		if (!this.saveOrUpdate(newEntity)) {
			throw new MyException(getEntityName() + "更新失败");
		}
		this.afterSaveOrUpdateEntity(newEntity, isNew);
		return newEntity;
	}

	public void initSaveOrUpdateEntity(T entity) {
	}

	/**
	 * 创建entity之前的操作
	 *
	 * @param entity
	 */
	public void beforeSaveEntity(T entity) {
		if (ObjectUtil.isNull(entity)) {
			throw new MyException(getEntityName() + "不能为null");
		}
		BeanUtil.setFieldValue(entity, "createTime", new Date());
	}

	/**
	 * 获取更新实体类时不更新的对象
	 *
	 * @return
	 */
	public List<String> getUpdateIgnoreKey() {
		return CollUtil.newArrayList();
	}

	/**
	 * 更新entity之前的操作
	 *
	 * @param entity
	 */
	public void beforeUpdateEntity(T entity, T old) {
		if (ObjectUtil.isNull(entity) || ObjectUtil.isNull(old)) {
			throw new MyException(getEntityName() + "不能为null");
		}
		BeanUtil.setFieldValue(entity, "updateTime", new Date());
	}

	/**
	 * 验证数据有效性
	 *
	 * @param entity
	 * @param isNew
	 */
	public void beforeSaveOrUpdateEntity(T entity, boolean isNew) {
		if (ObjectUtil.isNull(entity)) {
			throw new MyException(getEntityName() + "不能为null");
		}
	}

	public void afterSaveOrUpdateEntity(T entity, boolean isNew) {

	}

	public boolean updatePath(Long id, String oldPath, String parentPath, String pathKey) {
		return updatePath(id, oldPath, parentPath, pathKey, "#", true);
	}

	public boolean updatePath(Long id, String oldPath, String parentPath, String pathKey, String splitStr, Boolean round) {
		if (StrUtil.isBlank(splitStr)) {
			throw new MyException("拼接字符串不能为空");
		}
		List<String> ids = StrUtil.splitTrim(parentPath, splitStr);
		ids.add(Convert.toStr(id));
		String path = CollUtil.join(ids, splitStr);
		if (round) {
			path = StrUtil.format("{}{}{}", splitStr, path, splitStr);
		}
		if (StrUtil.equals(oldPath, path)) {
			return true;
		}
		//本级路由更新
		if (!this.update(
				new UpdateWrapper<T>()
						.set(pathKey, path)
						.eq(getIdKey(), id)
		)) {
			throw new MyException("数据路由更新失败");
		}
		if (StrUtil.isNotEmpty(oldPath)) {
			//下级路由更新
			this.update(
					new UpdateWrapper<T>()
							.setSql(StrUtil.format("{} = REPLACE({},'{}','{}')", pathKey, pathKey, oldPath, path))
							.eq(StrUtil.format("LOCATE('{}',{})", oldPath, pathKey), 1)
							.ne(pathKey, oldPath)
			);
		}
		return true;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean disableByIdList(Long userId, List<Long> idList) {
		return this.updateStatus(userId, idList, false);
	}

	public void beforeDisableByIdList(Long userId, List<Long> idList) {
	}

	public void afterDisableByIdList(Long userId, List<Long> idList) {
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean enableByIdList(Long userId, List<Long> idList) {
		return this.updateStatus(userId, idList, true);
	}

	public void beforeEnableByIdList(Long userId, List<Long> idList) {
	}

	public void afterEnableByIdList(Long userId, List<Long> idList) {
	}

	@Override
	public boolean updateStatus(Long userId, List<Long> idList, boolean status) {
		if (CollUtil.isEmpty(idList)) {
			return true;
		}
		if (status) {
			beforeEnableByIdList(userId, idList);
		} else {
			beforeDisableByIdList(userId, idList);
		}
		beforeUpdateStatus(userId, idList, status);

		boolean flag = this.updateByIdList(userId, idList, tUpdateWrapper -> tUpdateWrapper.set("status", status));
		if (!flag) {
			throw new MyException(getEntityName() + "更新状态失败");
		}

		afterUpdateStatus(userId, idList, status);
		if (status) {
			afterEnableByIdList(userId, idList);
		} else {
			afterDisableByIdList(userId, idList);
		}
		return true;
	}

	@Override
	public boolean updateByIdList(Long userId, List<Long> idList, Function<UpdateWrapper<T>, UpdateWrapper<T>>... funArray) {
		if (CollUtil.isEmpty(idList)) {
			return true;
		}
		//更新数据
		UpdateWrapper<T> updateWrapper = new UpdateWrapper<T>()
				.in(getIdKey(), idList);
		return this.updateByUserId(userId, updateWrapper, funArray);
	}

	@Override
	public boolean updateByUserId(Long userId, Function<UpdateWrapper<T>, UpdateWrapper<T>>... funArray) {
		return updateByUserId(userId, null, funArray);
	}

	@Override
	public boolean updateByUserId(Long userId, UpdateWrapper<T> updateWrapper, Function<UpdateWrapper<T>, UpdateWrapper<T>>... funArray) {
		if (ObjectUtil.isNull(updateWrapper)) {
			updateWrapper = new UpdateWrapper<T>();
		}
		//更新数据
		updateWrapper.set("update_user_id", userId).set("update_time", new Date());
		//代理方法
		if (ArrayUtil.isNotEmpty(funArray)) {
			for (Function<UpdateWrapper<T>, UpdateWrapper<T>> fun : funArray) {
				if (ObjectUtil.isNotNull(fun) && ObjectUtil.isNotNull(updateWrapper)) {
					updateWrapper = fun.apply(updateWrapper);
				}
			}
		}
		Assert.isFalse(updateWrapper.isEmptyOfWhere(), "全表更新是被禁止的");
		return this.update(updateWrapper);
	}

	public void beforeUpdateStatus(Long userId, List<Long> idList, boolean status) {
	}

	public void afterUpdateStatus(Long userId, List<Long> idList, boolean status) {
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean deleteByIdList(Long userId, List<Long> idList) {
		if (CollUtil.isEmpty(idList)) {
			return true;
		}
		beforeDeleteByIdList(userId, idList);
		//删除
		boolean result = deleteByFunc(userId, tUpdateWrapper -> tUpdateWrapper.in(getIdKey(), idList));
		if (!result) {
			throw new MyException("删除" + getEntityName() + "失败");
		}
		afterDeleteByIdList(userId, idList);
		return true;
	}

	public boolean deleteByFunc(Long userId, Function<UpdateWrapper<T>, UpdateWrapper<T>> func) {
		Assert.notNull(func, "处理代码不能为NULL");
		//删除数据
		UpdateWrapper<T> updateWrapper = new UpdateWrapper<T>()
				.set("delete_user_id", userId)
				.set("delete_time", new Date())
				.set("deleted", true);
		updateWrapper = func.apply(updateWrapper);
		Assert.isFalse(updateWrapper.isEmptyOfWhere(), "不允许全表删除");
		//更新
		return this.update(updateWrapper);
	}

	public void beforeDeleteByIdList(Long userId, List<Long> idList) {
	}

	public void afterDeleteByIdList(Long userId, List<Long> idList) {
	}

	public QueryWrapper<T> getBaseWrapper(QueryForm<N> form, boolean isOrder) {
		if (ObjectUtil.isNull(form)) {
			return new QueryWrapper<T>();
		}
		BaseParamsForm params = form.getParams();
		QueryWrapper<T> wrapper = form.getWrapper(this.currentModelClass(), isOrder);
		if (ObjectUtil.isNull(params)) {
			return wrapper;
		}
		Integer status = params.getStatus();
		Integer isAll = params.getIsAll();
		return wrapper.eq(ObjectUtil.notEqual(isAll, 1) && ObjectUtil.isNotNull(status), "status", status);
	}

	@Override
	public QueryWrapper<T> getSelectWrapper(QueryForm<N> form, boolean isOrder) {
		return this.getBaseWrapper(form, isOrder);
	}

	public <V> List<V> createTree(Object pid, List<V> list, String childrenKey, String idKey, String pidKey) {
		List<V> tree = CollUtil.newArrayList();
		Map<Object, V> map = new HashMap<>();
		//所有Id
		List ids = CollUtil.getFieldValues(list, idKey);
		List<V> voList = CollUtil.newArrayList(list);
		do {
			List<V> noList = CollUtil.newArrayList();
			CollUtil.forEach(voList.iterator(), (value, index) -> {
				Object voId = BeanUtil.getFieldValue(value, idKey);
				Object voPid = BeanUtil.getFieldValue(value, pidKey);
				initChildren(value, childrenKey);
				//存在上级
				if (CollUtil.contains(ids, voPid)) {
					//父节点
					V voParent = map.get(voPid);
					if (ObjectUtil.isNotNull(voParent)) {
						List children = initChildren(voParent, childrenKey);
						children.add(value);
						BeanUtil.setFieldValue(voParent, childrenKey, children);
					} else {
						noList.add(value);
					}
				} else {
					if (ObjectUtil.isNull(pid) || NumberUtil.equals(Convert.toBigDecimal(voPid), Convert.toBigDecimal(pid))) {
						tree.add(value);
					}
				}
				map.put(voId, value);
			});
			if (voList.size() == noList.size()) {
				break;
			}
			voList.removeAll(voList);
			voList.addAll(noList);
		} while (voList.size() > 0);
		return tree;
	}

	private List initChildren(Object object, String key) {
		List children = Convert.toList(BeanUtil.getFieldValue(object, key));
		if (ObjectUtil.isNull(children)) {
			children = CollUtil.newArrayList();
			BeanUtil.setFieldValue(object, key, children);
		}
		return children;
	}

	@Transactional(rollbackFor = Exception.class)
	public boolean deleteByList(Long userId, List list, String key) {
		if (CollUtil.isEmpty(list)) {
			//当成成功啦
			return true;
		}
		List<Long> idList = this.listObjs(
				new QueryWrapper<T>()
						.in(key, list)
						.select("DISTINCT " + getIdKey()),
				o -> Convert.toLong(o)
		);
		if (CollUtil.isEmpty(idList)) {
			//当成成功啦
			return true;
		}
		return this.deleteByIdList(userId, idList);
	}

	public void copyProperties(Object source, Object target, String... keys) {
		Map sourceMap = new HashMap();
		if (ObjectUtil.isNotNull(source)) {
			sourceMap = BeanUtil.beanToMap(source, sourceMap, false, true);
		}
		if (ObjectUtil.isNotNull(target)) {
			sourceMap = MapUtil.getAny(sourceMap, keys);
			BeanUtil.copyProperties(sourceMap, target);
		}
	}
}
